<?php
/**
* @file UserService.php
* Copyright (c) Friends Group. All rights reserved.
* $Id$
*
*/

require_once(realpath(dirname(__FILE__)) . '/classes/Account.php');
require_once(realpath(dirname(__FILE__)) . '/classes/UserOption.php');
require_once(realpath(dirname(__FILE__)) . '/classes/UserGroup.php');
require_once(realpath(dirname(__FILE__)) . '/classes/UserChatOption.php');
require_once(realpath(dirname(__FILE__)) . '/classes/Avatar.php');
require_once(realpath(dirname(__FILE__)) . '/classes/Status.php');
require_once(realpath(dirname(__FILE__)) . '/classes/UserStatus.php');
require_once(realpath(dirname(__FILE__)) . '/classes/FriendList.php');
require_once(realpath(dirname(__FILE__)) . '/classes/Role.php');
require_once(realpath(dirname(__FILE__)) . '/interfaces/IUserService.php');
require_once(realpath(dirname(__FILE__)) . '/DataService.php');
require_once(realpath(dirname(__FILE__)) . '/CommonService.php');
require_once(realpath(dirname(__FILE__)) . '/SystemConfiguration.php');
require_once(realpath(dirname(__FILE__)) . '/common/Validator.php');



class UserService implements IUserService 
{

    public function __construct()
    {
        $this -> repo = new DataService();
    }
    
    //xunglv thêm mấy hàm hỗ trợ
    function isExistUserId($userID)
    {
        try
        {
            $tmp = $this -> findUserById($userID);
            return true;
        }
        catch(Exception $e)
        { 
            return false;
        }
    
    }
    
    function isExistUsername($userName)
    {
        try
        {
            $tmp = $this -> findUserByName($userName);
            return true;
        }
        catch(Exception $e)
        { 
            return false;
        }
    }
    
    function isExistGroupName($userId, $groupName)
    {
        // lấy tất cả group của user lên để so sánh
        $groups = $this -> getUserGroups($userId);
        if (!$groups)
            return false;
        
        foreach($groups as $g)
        {
            if ($g -> getGroupName() == $groupName)
                return true;
        }
        return false;
    }
    
    function validateUser($user)
    {
        if(!Validator::validate($user -> getEmail(), "email"))
            throw new Exception("Email không hợp lệ");
        elseif(!Validator::validate($user -> getPhone(), "phone"))
            throw new Exception("Số điện thoại không hợp lệ");
        
        Validator::validatePassword($user -> getPassword());
        Validator::validateUser($user -> getNickname());
        
        return true;
    }
    
    /**
     * @ReturnType void
     * @ParamType user 
     * Tạo 1 user mới
     */
    public function createUser($user) 
    {
        if(get_class($user) != 'Account')
            throw new Exception("Kiểu tham số không hợp lệ. User phải là 1 đối tượng Account");
        
        // kiểm tra dữ liệu
        //kiểm tra tồn tại username - xunglv
        if ($this -> isExistUsername($user -> getNickname()))
            throw new Exception("Tên {$user -> getNickname()} đã được sử dụng");
        
        $this -> validateUser($user);
        
        $st = new SystemConfiguration();
        $cm = new CommonService();
        
        if($st -> getSetting("user-must-activate") == "1")
        {
            // tao activation key
            $user -> setActivationKey(md5($user -> getNickname()));
            $user -> setAccountStatus("");
        }
        else
        {
            $user -> setActivationKey("");
            $user -> setAccountStatus(1);
        }
        
        try
        {
            //mã hóa password
            $user -> setPassword(sha1($user -> getPassword()));
            
            // tạo user
            $this -> repo -> create($user);
            $userId = $user -> getID();
            
            // khởi tạo options cho user
            $this -> setUserOption($userId, "show-email", "1");
            $this -> setUserOption($userId, "show-avatar", "1");
            $this -> setUserOption($userId, "save-message", "0");
            $this -> setUserOption($userId, "last-status", "Online");
            $this -> setUserOption($userId, "emoticon-theme", $st -> getSetting("emoticon-theme"));
            
            // khởi tạo user status
            $this -> setUserStatus($userId, $cm -> getStatusByName("Online"));
            
            $this -> createUserGroup($userId, new UserGroup($userId, "Buddy"));
            
            // mặc định user mới tạo sẽ có Role là user
            $this -> setUserRole($userId, $cm -> getRoleByName("User"));
            
        }
        catch(Exception $ex)
        {
            throw new Exception($ex -> getMessage());
        }
    }

    /**
     * @ReturnType void
     * @ParamType userId 
     * Xóa user theo ID
     */
    public function deleteUser($userId) 
    {
        $user = $this -> findUserById($userId);
        $this -> repo -> delete($user);
    }

    /**
     * @ReturnType classes.Account
     * @ParamType userId 
     * Tìm user theo ID
     */
    public function findUserById($userId) 
    {
        $user = $this -> repo -> getFirst("SELECT * FROM Account WHERE id = '$userId'", "Account");
        
        if($user == null)
            throw new Exception("Không tìm thấy UserId #" . $userId);
        
        return $user;
    }

    /**
     * @ReturnType classes.Account
     * @ParamType userName 
     * Tìm user theo username
     */
    public function findUserByName($userName)
    {
        $user = $this -> repo -> getFirst("SELECT * FROM Account WHERE nickname = '$userName'", "Account");
        if($user == null)
            throw new Exception("Không tìm thấy User " . $userName);
        
        return $user;
    }

    /**
     * @ReturnType void
     * @ParamType user 
     * Cập nhật thông tin user
     */
    public function updateUser($user) 
    {
        // kiểm tra các thông tin mới của user!
        if(get_class($user) != 'Account')
            throw new Exception("Kiểu tham số không hợp lệ. User phải là 1 đối tượng Account");
        
        // userId của user vẫn giữ nguyên
        $this -> repo -> update($user);
    }
    
    public function logoutInactiveUser()
    {
        $cm = new CommonService();
        $st = new SystemConfiguration();
        
        $inactiveTimeout = intval($st -> getSetting("inactive-timeout"));
        $timeout = time(888 gkfg g  ) - $inactiveTimeout;
        $off = $cm -> getStatusByName("Offline");
        
        $users = $this -> repo -> getList("SELECT * FROM Account JOIN UserStatus ON UserStatus.accountID = Account.ID WHERE UserStatus.statusID <> '{$off -> getID()}' AND Account.lastAccessTime < $timeout", "Account");
        
        if($users != null)
        {
            foreach ($users as $user)
            {
                $this -> setUserOption($user -> getID(), "last-status", $this -> getUserStatus($user -> getID()) -> getStatusText());
                $this -> setUserStatus($user -> getID(), $cm -> getStatusByName("Offline"));
            }
        }
    }
    
    public function updateLastAccessTime($userId)
    {
        $user = $this -> findUserById($userId);
        $user -> setLastAccessTime(time());
        $this -> updateUser($user);
    }
    
    /**
     * @ReturnType void
     * @ParamType userId 
     * @ParamType group 
     * Tạo 1 user group 
     */
    public function createUserGroup($userId, $group)
    {
        if (get_class($group) != "UserGroup")
            throw new Exception("Kiểu tham số không hợp lệ. group phải là 1 đối tượng UserGroup");
        
        $this -> findUserById($userId);
        $group -> setAccountID($userId);
        
        // một user thì không thể có 2 group cùng tên
        if ($this -> isExistGroupName($userId, $group -> getGroupName()))
            throw new Exception("Không thể tạo 2 group cùng tên cho 1 user");
        
        $this -> repo -> create($group);
    }

    public function findUserGroupById($userId, $groupId)
    {
        $group = $this -> repo -> getFirst("SELECT * FROM UserGroup WHERE accountID = '$userId' AND ID = '$groupId'","UserGroup");
        if($group == null)
            throw new Exception("Không tìm thấy group #$groupId");
        
        return $group;
    }
    
    public function findUserGroupByName($userId, $groupName)
    {
        $group = $this -> repo -> getFirst("SELECT * FROM UserGroup WHERE accountID = '$userId' AND groupName = '$groupName'","UserGroup");
        if($group == null)
            throw new Exception("Không tìm thấy group #$groupName");
        
        return $group;
    }
    
    /**
     * @ReturnType Object
     * @ParamType userId 
     * Lấy danh sách tất cả group của user
     */
    public function getUserGroups($userId) 
    {
        return $this -> repo -> getList("SELECT * FROM UserGroup WHERE accountID = '$userId'","UserGroup");
    }

    /**
     * @ReturnType void
     * @ParamType userId 
     * @ParamType group 
     * Xóa user group
     */
    public function deleteUserGroup($userId, $group) 
    {
        $user = $this -> findUserById($userId);
        
        if($userId != $group -> getAccountID())
        {
           throw new Exception("Bạn có chắc user #{$user -> getNickname()} có group #{$group -> getGroupName()}?");
        }
        
        $this -> repo -> delete($group);
    }
    
    public function updateUserGroup($userId, $group)
    {
        $user = $this -> findUserById($userId);
        
        if($userId != $group -> getAccountID())
        {
           throw new Exception("Bạn có chắc user #{$user -> getNickname()} có group #{$group -> getGroupName()}?");
        }
        
        if ($this -> isExistGroupName($userId, $group -> getGroupName()))
            throw new Exception("Không thể tạo 2 group cùng tên cho 1 user");
        
        $this -> repo -> update($group);
    }
    
    /**
     * @ReturnType void
     * @ParamType userId 
     * @ParamType friendId 
     * Thêm user friend
     */
    public function addUserFriend($userId, $friendId, $group) 
    {
        // kiểm tra sự tồn tại của user id
        $user = $this -> findUserById($userId);
        $friend = $this -> findUserById($friendId);
        $groupId = $group -> getID();
        
        //if($user -> getID() == $friend -> getID())
        //    throw new Exception("Bạn không thể add chính mình");
        
        if($this -> isExistFriendInGroup($userId, $friendId, $group))
            throw new Exception("User #" . $friend -> getNickname() . " đã có trong group #" . $group -> getGroupName());
        
        if($this -> isExistUserFriend($userId, $friendId))
            throw new Exception("User #" . $friend -> getNickname() . " đã có trong danh sách friend của bạn");
        
        $friendList = new FriendList($userId, $friendId, $groupId, $friend -> getNickname());
        $this -> repo -> create($friendList);
    }

    public function isExistUserFriend($userId, $friendId)
    {
        $friend = $this -> repo -> getFirst("SELECT * FROM Account JOIN FriendList ON Account.ID = FriendList.friendAccountID WHERE FriendList.accountID = '$userId' AND FriendList.friendAccountID = '$friendId'","Account");
        return $friend != null;
    }
    
    /**
     * @ReturnType Object
     * @ParamType userId 
     * Lấy danh sách tất cả friend của user
     */
    public function getUserFriends($userId) 
    {
        $this -> findUserById($userId);
        
        return $this -> repo -> getList("SELECT * FROM Account JOIN FriendList ON Account.ID = FriendList.friendAccountID WHERE FriendList.accountID = '$userId'","Account");
    }

    public function getFriendListInfo($userId, $friendId)
    {
        $this -> findUserById($userId);
        $this -> findUserById($friendId);
        
        return $this -> repo -> getFirst("SELECT * FROM FriendList WHERE accountId = '$userId' AND friendAccountID = '$friendId'", "FriendList");
    }
    
    public function changeBuddyAlias($userId, $friendId, $newAlias)
    {
        $this -> findUserById($userId);
        
        $friend = $this -> getFriendListInfo($userId, $friendId);
        $friend -> setBuddyAliasName($newAlias);
        
        $this -> repo -> update($friend, array("accountId" => "$userId", "friendAccountID" => "$friendId"));
    }
    
    /**
     * @ReturnType void
     * @ParamType userId 
     * @ParamType friendId 
     * Xóa friend khỏi danh sách friend
     */
    public function removeUserFriend($userId, $friendId) 
    {
        // kiểm tra sự tồn tại của user id
        $user = $this -> findUserById($userId);
        $friend = $this -> findUserById($friendId);
        
        $friendList = $this -> repo -> getFirst("SELECT * FROM FriendList WHERE accountID = '$userId' AND friendAccountID = '$friendId'", "FriendList");
        if($friendList == null)
            throw new Exception("Không tìm thấy friend #{$friend -> getNickname()} trong danh sách friend của user #{$user -> getNickname()}");
        
        $where = array("friendAccountID" => $friendId, "accountID" => $userId);
        $this -> repo -> delete($friendList, $where);
    }

    /**
     * @ReturnType void
     * @ParamType userId 
     * @ParamType friendId 
     * @ParamType group 
     * Di chuyển friend sang nhóm khác
     */
    public function moveFriendToGroup($userId, $friendId, $group) 
    {
        throw new Exception("Not implement yet");
    }

    public function isExistFriendInGroup($userId, $friendId, $group)
    {
        $friend = $this -> repo -> getFirst("SELECT * FROM Account JOIN FriendList ON Account.ID = FriendList.friendAccountID WHERE FriendList.accountID = '$userId' and FriendList.userGroupId = '{$group -> getID()}' AND FriendList.friendAccountID = '$friendId'","Account");
        return $friend != null;
    }
    
    /**
     * @ReturnType Object
     * @ParamType userId 
     * @ParamType groupId 
     * Lấy danh sách tất cả friend trong 1 group
     */
    public function getFriendsInGroup($userId, $group) 
    {
        $accountID = $group -> getAccountID();
        $user = $this -> findUserById($userId);
        
        if($userId != $accountID)
            throw new Exception("Bạn có chắc user #{$user -> getNickname()} có group #{$group -> getGroupName()}?");
        
        return $this -> repo -> getList("SELECT * FROM Account JOIN FriendList ON Account.ID = FriendList.friendAccountID WHERE FriendList.accountID = '$userId' AND FriendList.userGroupId = '{$group -> getID()}'","Account");
    }
    
    
    /**
     * @ReturnType classes.Account
     * Lấy danh sách các user đang online
     */
    public function getAllUsersOnline() 
    {
        $c = new CommonService();
        $off = $c -> getStatusByName("Offline");
        $invi = $c -> getStatusByName("Invisible");
        
        return $this -> repo -> getList("SELECT * FROM Account JOIN UserStatus ON UserStatus.accountID = Account.ID WHERE UserStatus.statusID <> '{$off -> getID()}' AND UserStatus.statusID <> '{$invi -> getID()}'", "Account");
    }

    /**
     * @ReturnType void
     * @ParamType account 
     * Kích hoạt tài khoản
     */
    public function activateAccount($account, $checkKey) 
    {
        if($account == null)
            throw new Exception("account cannot be NULL.");
        
        $accountStatus = $account -> getAccountStatus();
        if($account -> getAccountStatus() == "0" || empty($accountStatus))
            throw new Exception("Tài khoản {$account -> getNickname()} đã được kích hoạt");
        
        $ackey = $account -> getActivationKey();
        if($ackey != null && !empty($ackey) && $ackey == $checkKey)
        {
            $account -> setActivationKey("");
            $account -> setAccountStatus(1);
            
            return true;
        }
        
        throw new Exception("Không thể kích hoạt tài khoản");
    }

    /**
     * @ReturnType classes.UserOption
     * @ParamType userId 
     * Lấy thông tin cấu hính của user
     */
    public function getAllUserOptions($userId) 
    {
        return $this -> repo -> getList("SELECT * FROM UserOption WHERE AccountID = '$userId'", "UserOption");
    }

    /**
     * @ReturnType classes.UserOption
     * @ParamType userId 
     * Lấy thông tin cấu hính của user
     */
    public function getAllUserChatOptions($userId, $friendId) 
    {
        return $this -> repo -> getList("SELECT * FROM UserChatOption WHERE accountID = '$userId' AND friendAccountID = '$friendId'", "UserChatOption");
    }
    
    /**
     * @ReturnType classes.UserOption
     * @ParamType userId 
     * Lấy thông tin cấu hính của user
     */
    public function getUserOption($userId, $optionName) 
    {
        $op = $this -> repo -> getFirst("SELECT * FROM UserOption WHERE AccountID = '$userId' AND optionName = '$optionName'", "UserOption");
        if($op)
            return $op -> getOptionValue();
        
        return "";
    }

    /**
     * @ReturnType classes.UserOption
     * @ParamType userId 
     * Lấy thông tin cấu hính của user
     */
    public function setUserOption($userId, $optionName, $optionValue) 
    {
        $userOption = $this -> repo -> getFirst("SELECT * FROM UserOption WHERE accountID = '$userId' AND optionName = '$optionName'", "UserOption");
        if($userOption == null)
            $this -> repo -> create(new UserOption($userId, $optionName, $optionValue));
        else
        {
            $userOption -> setOptionValue($optionValue);
            $this -> repo -> update($userOption);
        }
    }

    /**
     * @ReturnType classes.UserChatOption
     * @ParamType userId 
     * @ParamType friendId 
     * Lấy thông tin cấu hình chat
     */
    public function getUserChatOption($userId, $friendId, $optionName) 
    {
        $op = $this -> repo -> getFirst("SELECT * FROM UserChatOption WHERE accountID = '$userId' AND friendAccountID = '$friendId' AND optionName = '$optionName'", "UserChatOption");
        if($op)
            return $op -> getOptionValue();
        
        return "";
    }

    /**
     * @ReturnType classes.UserOption
     * @ParamType userId 
     * Lấy thông tin cấu hính của user
     */
    public function setUserChatOption($userId, $friendId, $optionName, $optionValue) 
    {
        $userChatOption = $this -> repo -> getFirst("SELECT * FROM UserChatOption WHERE accountID = '$userId' AND friendAccountID = '$friendId' AND optionName = '$optionName'", "UserChatOption");
        if($userChatOption == null)
            $this -> repo -> create(new UserChatOption($userId, $friendId, $optionName, $optionValue));
        else
        {
            $userChatOption -> setOptionValue($optionValue);
            $this -> repo -> update($userChatOption);
        }
    }
    
    /**
     * @ReturnType classes.Avatar
     * @ParamType userId 
     * Lấy thong tin avatar của user
     */
    public function getUserAvatar($userId) 
    {
        $user = $this -> findUserById($userId);
        $avatar = $this -> repo -> getFirst("SELECT * FROM Avatar WHERE id = {$user -> getAvatarID()}", "Avatar");
        return $avatar;
    }

    /**
     * @ReturnType classes.Role
     * @ParamType userId 
     * Lấy thông tin về quyền của user
     */
    public function getUserRole($userId) 
    {
        $user = $this -> findUserById($userId);
        $role = $this -> repo -> getFirst("SELECT * FROM Role WHERE id = {$user -> getRoleID()}", "Role");
        return $role;
    }

    /**
     * @ReturnType void
     * @ParamType userId 
     * @ParamType avatar 
     * Lưu user avatar
     */
    public function setUserAvatar($userId, $avatar) 
    {
        $user = $this -> findUserById($userId);
        $user -> setAvatarID($avatar -> getID());
        //var_dump($user);
        $this -> repo -> update($user);
    }

    /**
     * @ReturnType void
     * @ParamType userId 
     * @ParamType role 
     * Set quyền cho user
     */
    public function setUserRole($userId, $role) 
    {
        $user = $this -> findUserById($userId);
        $user -> setRoleID($role -> getID());
        $this -> repo -> update($user);
    }

    /**
     * @ReturnType Object
     * @ParamType status 
     * Lấy danh sách user theo trạng thái
     */
    public function getUsersByStatus($status) 
    {
        if($status == null)
            throw new Exception("Status cannot be NULL.");
        
        return $this -> repo -> getList("SELECT * FROM Account JOIN UserStatus ON UserStatus.accountID = Account.ID WHERE UserStatus.statusID = '{$status -> getID()}'", "Account");
    }

    /**
     * @ReturnType void
     * @ParamType userId 
     * @ParamType status 
     * Set trạng thái cho user
     */
    public function setUserStatus($userId, $status, $statusText = "") 
    {
        if($status == null)
            throw new Exception("status cannot be NULL.");
        
        $sta = $this -> repo -> getFirst("SELECT * FROM Status WHERE ID = '{$status -> getID()}'", "Status");
        if(!$sta)
            throw new Exception("Status chưa được định nghĩa");
        
        if($status -> getStatus() != "Custom")
            $statusText = $status -> getStatus();
        
        $userStatus = $this -> repo -> getFirst("SELECT * FROM UserStatus WHERE accountID = '$userId'", "UserStatus");
        
        if($userStatus == null)
            $this -> repo -> create(new UserStatus($userId, $status -> getID(), $statusText));
        else
        {
            $userStatus -> setStatusID($status -> getID());
            $userStatus -> setStatusText($statusText);
            $this -> repo -> update($userStatus, array("accountID" => $userId));
        }
    }

    /**
     * @ReturnType Object
     * @ParamType userId 
     * Lấy trạng thái của user
     */
    public function getUserStatus($userId) 
    {
        return $userStatus = $this -> repo -> getFirst("SELECT * FROM UserStatus WHERE accountID = '$userId'", "UserStatus");
    }
    
    public function isActivationKeyTimeout($user)
    {
        $st = new SystemConfiguration();
        $keyTimeout = intval($st -> getSetting("activation-key-life"));
        $registered = strtotime($user -> getRegistered());
        $now = strtotime("now");
        return ($now - $registered)/3600 > $keyTimeout;
    }

}
$u=new UserService();
$u->logoutInactiveUser();
?>
